package fr.umlv.m2.jee.spij.service.lesson.impl;

import fr.umlv.m2.jee.spij.persistence.actor.StudentLesson;
import fr.umlv.m2.jee.spij.persistence.actor.TeacherLesson;
import fr.umlv.m2.jee.spij.persistence.actor.dao.HibernateStudentLessonDAO;
import fr.umlv.m2.jee.spij.persistence.actor.dao.HibernateTeacherLessonDAO;
import fr.umlv.m2.jee.spij.persistence.education.Lesson;
import fr.umlv.m2.jee.spij.persistence.education.Unit;
import fr.umlv.m2.jee.spij.persistence.education.dao.HibernateLessonDAO;
import fr.umlv.m2.jee.spij.persistence.education.dao.HibernateUnitDAO;
import fr.umlv.m2.jee.spij.service.ldap.authentification.DisplayActor;
import fr.umlv.m2.jee.spij.service.lesson.DisplayLesson;
import fr.umlv.m2.jee.spij.service.lesson.IDisplayLessonService;
import org.joda.time.DateTime;
import org.joda.time.Interval;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.binding.message.MessageBuilder;
import org.springframework.binding.message.MessageContext;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * @author thomas magny
 * @author mathieu mazzolini
 */
@Service("defaultDisplayLessonService")
public class DefaultDisplayLessonService implements IDisplayLessonService {

    @Autowired
    private HibernateUnitDAO unitDAO;

    @Autowired
    private HibernateStudentLessonDAO studentLessonDAO;

    @Autowired
    private HibernateTeacherLessonDAO teacherLessonDAO;

    @Autowired
    private HibernateLessonDAO lessonDAO;

    /**
     * Empty constructor
     */
    DefaultDisplayLessonService() {
    }

    @Override
    @Transactional(readOnly = true)
    public List<DisplayLesson> getAllDisplayLesson(long unitId, MessageContext context) {
        Unit unit = unitDAO.findById(unitId);
        final List<Lesson> lessons = unit.getLessons();
        final List<DisplayLesson> displayLessons = new ArrayList<DisplayLesson>();

        if (lessons == null) {
            context.addMessage(new MessageBuilder().error().code("lessons.empty").build());
        } else {

            for (Lesson lesson : lessons) {
                DisplayLesson displayLesson = new DisplayLesson(lesson);
                if (unit != null) {
                    displayLesson.setUnitName(unit.getName());
                }
                displayLessons.add(displayLesson);
            }
        }
        return displayLessons;
    }

    @Override
    @Transactional(readOnly = true)
    public List<DisplayLesson> getAllDisplayLessonPerUser(long unitId, DisplayActor currentUser, MessageContext context) {
        if (currentUser.isTeacher() || currentUser.isStudent()) {
            Unit unit = unitDAO.findById(unitId);
            final List<Lesson> lessons = lessonDAO.findByLdapActorAndUnitId(unitId, currentUser.getLogin(), currentUser.getType());
            final List<DisplayLesson> displayLessons = new ArrayList<DisplayLesson>(lessons.size());

            if (lessons.isEmpty()) {
                context.addMessage(new MessageBuilder().error().code("lessons.empty").build());
            } else {
                for (Lesson lesson : lessons) {
                    DisplayLesson displayLesson = new DisplayLesson(lesson);
                    if (unit != null) {
                        displayLesson.setUnitName(unit.getName());
                    }
                    displayLessons.add(displayLesson);
                }
            }
            return displayLessons;
        }

        return getAllDisplayLesson(unitId, context);
    }

    @Override
    @Transactional(readOnly = true)
    public DisplayLesson getLessonById(long id, MessageContext context) {
        final Lesson lesson = lessonDAO.findById(id); // should never return null
        final DisplayLesson displayLesson = new DisplayLesson(lesson);

        if (lesson == null) {
            context.addMessage(new MessageBuilder().error().code("lesson.empty").build());
        } else {
            if (lesson.getUnit() != null) {
                displayLesson.setUnitName(unitDAO.findById(lesson.getUnit()).getName());
            }
        }
        return displayLesson;
    }

    @Override
    @Transactional(readOnly = false)
    public boolean createLesson(DisplayLesson createdLesson, long unitId,
                                MessageContext context) {
        Lesson lesson = new Lesson();
        lesson.setUnit(unitId);
        lesson.setSubject(createdLesson.getSubject());
        lesson.setContain(createdLesson.getContain());
        lesson.setLinkSupport(createdLesson.getLinkSupport());

        Date date = createdLesson.getDate();
        DateTime startTime = new DateTime(date.getTime());
        startTime = startTime.plus(createdLesson.getStartTime().getTime());
        DateTime endTime = new DateTime(date.getTime());
        endTime = endTime.plus(createdLesson.getEndTime().getTime());

        if (startTime.isAfter(endTime)) {
            context.addMessage(new MessageBuilder().error().code("lesson.startTime.isAfter.endTime").build());
            return false;
        }

        final Interval duration = new Interval(startTime, endTime);
        lesson.setDuration(duration);
        lessonDAO.persist(lesson);

        return true;
    }

    @Override
    @Transactional(readOnly = false)
    public boolean uploadLesson(DisplayLesson createdLesson,
                                MessageContext context, byte[] fileData) {
        Lesson lesson = lessonDAO.findById(createdLesson.getId());
        if (fileData != null) {
            // System.out.println("DefaultDisplayLessonService.createLesson() : file size -> " + fileData.length);
            lesson.setFileData(fileData);
        }
        lessonDAO.merge(lesson);
        return true;
    }

    @Override
    @Transactional(readOnly = false)
    public boolean updateLessonById(DisplayLesson modifiedLesson, final MessageContext context) {
        Lesson lesson = lessonDAO.findById(modifiedLesson.getId());
        boolean hasBeenModified = false;

        if (lesson != null) {
            if (!lesson.getSubject().equals(modifiedLesson.getSubject())) {
                hasBeenModified = true;
                lesson.setSubject(modifiedLesson.getSubject());
            }
            if (!lesson.getContain().equals(modifiedLesson.getContain())) {
                hasBeenModified = true;
                lesson.setContain(modifiedLesson.getContain());
            }
            if (!lesson.getLinkSupport().equals(modifiedLesson.getLinkSupport())) {
                hasBeenModified = true;
                lesson.setLinkSupport(modifiedLesson.getLinkSupport());
            }

            //Duration update
            Date date = modifiedLesson.getDate();
            DateTime startTime = new DateTime(date.getTime());
            startTime = startTime.plus(modifiedLesson.getStartTime().getTime());

            DateTime endTime = new DateTime(date.getTime());
            endTime = endTime.plus(modifiedLesson.getEndTime().getTime());

            DateTime previousStartDate = lesson.getDuration().getStart();
            DateTime previousEndDate = lesson.getDuration().getEnd();

            if (startTime.isAfter(endTime)) {
                context.addMessage(new MessageBuilder().error().code("lesson.startTime.isAfter.endTime").build());
                return false;
            }

            if (!previousStartDate.equals(startTime) || !previousEndDate.equals(endTime)) {
                hasBeenModified = true;
                lesson.setDuration(new Interval(startTime, endTime));
            }

            lessonDAO.merge(lesson);
        }

        if (!hasBeenModified) {
            context.addMessage(new MessageBuilder().error().code("updateLessonFail").build());
        }

        return hasBeenModified;
    }

    @Override
    @Transactional(readOnly = false)
    public boolean deleteLessonById(long id, final MessageContext context) {
        Lesson lesson = lessonDAO.findById(id);
        List<StudentLesson> students = lesson.getStudents();
        for (StudentLesson studentLesson : students) {
            studentLessonDAO.remove(studentLesson);
            studentLessonDAO.flush();
        }
        List<TeacherLesson> teachers = lesson.getTeachers();
        for (TeacherLesson teacherLesson : teachers) {
            teacherLessonDAO.remove(teacherLesson);
            teacherLessonDAO.flush();
        }

        lessonDAO.remove(lesson);
        lessonDAO.flush();
        return true;
    }

}

